use std::sync::Arc;
use std::time::Duration;

use async_trait::async_trait;
use libunftp::notification::{EventMeta, PresenceEvent, PresenceListener};
use log::info;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
use unftp_auth_jsonfile::JsonFileAuthenticator;
use unftp_sbe_fs::ServerExt;

pub struct FtpConfig {
    pub ftp_home: String,
    pub port: String,
    pub host: String,
    pub ftp_auth: FtpAuth,
    // 用来接受关闭命令的消费者,由于启动关闭只会调用一次所以用oneshot即可
    pub shutdown_rx: oneshot::Receiver<u64>,
    // 用来发送ftp server的用户事件
    pub presence_notify_tx: mpsc::Sender<PresenceEvent>,
    pub ctx: egui::Context,
}

#[derive(Debug)]
struct PresenceNotify(mpsc::Sender<PresenceEvent>, egui::Context);

#[derive(serde::Deserialize, serde::Serialize, Clone)]
pub struct FtpAuth {
    pub username: String,
    pub password: String,
}

#[async_trait]
impl PresenceListener for PresenceNotify {
    async fn receive_presence_event(&self, e: PresenceEvent, _m: EventMeta) {
        if let Err(e) = self.0.send(e).await {
            info!("事件监听发送失败:{}", e);
        } else {
            self.1.request_repaint();
        }
    }
}

/// 启动ftp服务器
pub fn run_ftp(ftp_config: FtpConfig) {
    let auth_json = serde_json::to_string(&vec![ftp_config.ftp_auth]).unwrap();
    info!("{}", auth_json);
    let authenticator = JsonFileAuthenticator::from_json(auth_json).unwrap();

    let ftp_server = async move {
        libunftp::Server::with_fs(ftp_config.ftp_home)
            .greeting("Welcome to my FTP server")
            .passive_ports(50000..65535)
            .authenticator(Arc::new(authenticator))
            .notify_presence(PresenceNotify(ftp_config.presence_notify_tx, ftp_config.ctx))
            .shutdown_indicator(async move {
                match ftp_config.shutdown_rx.await {
                    Ok(time) => {
                        if time != 0 {
                            tokio::time::sleep(Duration::from_secs(time)).await
                        }
                    }
                    Err(_) => info!("the sender dropped"),
                }
                libunftp::options::Shutdown::new()
                    .grace_period(Duration::from_secs(5))
            })
            .listen(format!("{}:{}", ftp_config.host, ftp_config.port)).await.unwrap();
    };

    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(ftp_server);
}